{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Illustrates how to sniff USB traffic and interpret it:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Connect:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import phywhisperer.usb as pw\n",
    "phy = pw.Usb()\n",
    "phy.con(program_fpga=True)\n",
    "phy.set_power_source(\"host\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Power-off target to allow PhyWhisperer to autodetect its speed:\n",
    "Ensure the target device is connected to the PhyWhisperer.\n",
    "We'll turn off the target's power so that the PhyWhisperer can be programmed before turning the target back on. We do this because we'll be capturing what the target does when it's first turned on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "phy.set_power_source(\"off\")\n",
    "time.sleep(0.5)\n",
    "phy.reset_fpga()\n",
    "phy.set_usb_mode('auto')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tell PhyWhisperer how many events to capture:\n",
    "In this example we'll read the captured data several cells after it's captured, so unless our USB target is very slow, we won't be able to capture more than what the PhyWhisperer's internal storage can hold (8188 USB events)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.set_capture_size(8188)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Arm the PhyWhisperer:\n",
    "You should see the blue ARM LED turn on to reflect the armed status.\n",
    "In this example we don't want to generate an external trigger, we only want to capture the USB traffic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.arm()\n",
    "phy.set_trigger(enable=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Program the pattern match:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.set_pattern(pattern=[0x2d, 0x00], mask=[0xff, 0xff])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Power up the target:\n",
    "Now that PhyWhisperer is programmed, power up the target. PW should auto-detect the correct speed, and the capture should be triggered."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.set_power_source(\"host\")\n",
    "#Let device enumerate\n",
    "time.sleep(1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ensure correct USB speed was detected:\n",
    "If the assertion fails, try setting the USB speed manually with set_usb_mode()."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert (phy.get_usb_mode() == 'FS')\n",
    "#assert (phy.get_usb_mode() == 'LS')\n",
    "#assert (phy.get_usb_mode() == 'HS')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Read what was captured:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "raw = phy.read_capture_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Check the capture memory status:\n",
    "No overflow or underflow events should have occured."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.check_fifo_errors()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpret the captured data:\n",
    "The pattern match byte which triggered the captured isn't recorded; let's add it back it so that all of the captured USB data can be properly interpreted:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.addpattern = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we split the raw captured data and timestamps into packets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "packets = phy.split_packets(raw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can use ViewSB to interpret the packets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.print_packets(packets[:100])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next few sections in this notebook show how to get more out of all the data that's captured by PhyWhisperer."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting more granular time information from the raw capture data:\n",
    "For each packet, the output of `print_packets()` gives you the time when the `rx_active` went high at the start of the packet. It's possible to go more granular and get the exact time that a particular byte of a packet was received from the USB PHY. Let's look at one particular packet. If `packets[4]` isn't an interesting multi-byte packet in your capture, try a different index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "packet_index = 4\n",
    "phy.print_packets([packets[packet_index]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here are all the raw attributes of that packet: its timestamp, size, flags, and contents (flags will be discussed in the next section):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "timestamp = packets[packet_index]['timestamp']\n",
    "print(\"Timestamp=%d\" % timestamp)\n",
    "print(\"Size=%d bytes\" % packets[packet_index]['size'])\n",
    "print(\"Flags=%s\" % packets[packet_index]['flags'])\n",
    "print(\"Raw content: \", end='')\n",
    "for byte in packets[packet_index]['contents']:\n",
    "    print(hex(byte), end=' ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want to find out what time each byte of the packet was received, we could look directly at the `raw` data that we read from the PhyWhisperer, but using the `split_data()` function to parse the raw data will make it easier to find what we're looking for.\n",
    "`split_data()` returns 4 lists:\n",
    "1. a data timestamp list\n",
    "2. a list of associated data events\n",
    "3. a USB status timestamp list\n",
    "4. a list of associated status events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "datatimes, databytes, stattimes, statbytes = phy.split_data(raw)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `timestamp` of our packet corresponds to the `rx_active` line going high and this happens prior to data being received, so we'll find that timestamp in our USB status timestamp list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "statindex = stattimes.index(timestamp)\n",
    "print(\"Status index: %d; status byte: %s\" % (statindex, hex(statbytes[statindex])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The status byte represents 5 status lines that are captured from the USB PHY: \n",
    "- rx active\n",
    "- rx error\n",
    "- session valid\n",
    "- session end\n",
    "- vbus valid\n",
    "\n",
    "(See the [Microchip USB3500 datasheet](https://www.microchip.com/wwwproducts/en/USB3500) for definitions of what these mean.)\n",
    "\n",
    "The status byte received at the start of a packet is always 0x15. \n",
    "Use `print_flags()` to parse the bitfields of a status byte:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.print_flags(statbytes[statindex])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we're interested in the packet data timestamps. These will follow shortly after this status timestamp. We can just look for the first data timestamp which is greater than this status timestamp:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "searchtime = timestamp\n",
    "while searchtime <= datatimes[-1]:\n",
    "    try:\n",
    "        data_start_index = datatimes.index(searchtime)\n",
    "        print(\"Found data timestamp: %d\" % data_start_index)\n",
    "        break\n",
    "    except:\n",
    "        searchtime += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We've now located our raw packet bytes within the list of all data bytes and data timestamps received from the PhyWhisperer. Now we can see at what time each byte was received from the USB phy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(data_start_index, data_start_index+10):\n",
    "    print('Timestamp: %d   Data: %s' % (datatimes[i], hex(databytes[i])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The data is the same that we got from `packets[packet_index]['contents']`, but now we have the timestamp for each byte."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# USB status flags:\n",
    "In the output of `print_packets()` you may have noticed the empty space within brackets at the start of every line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.print_packets(packets[:5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is where we would note when the USB status bits diverge from their expected values during a packet. With a well-behaved target, this field should always be blank.\n",
    "\n",
    "Let's simulate what would happen if this were not the case. Imagine you are using PhyWhisperer to attack the target and are successful at causing the USB phy to flag an Rx Error event.\n",
    "\n",
    "To simulate this, we go through the raw data captured by the PhyWhisperer. We find the first DATA command and set its Rx Error status bit (see `software/phywhisperer/firmware/defines.v` for definitions of the bitfields)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for rawentry in raw:\n",
    "    command = rawentry[2] & 0x3\n",
    "    if (command == phy.FE_FIFO_CMD_DATA):\n",
    "        rawentry[0] += 2**phy.FE_FIFO_RXERROR_BIT\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We then re-parse and print the raw data. You should see the error flag 'E' on the first packet:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "corrupted_packets = phy.split_packets(raw)\n",
    "phy.print_packets(corrupted_packets[0:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also see this at a more granular level in the `split_data` output:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "datatimes, databytes, stattimes, statbytes = phy.split_data(raw)\n",
    "phy.print_flags(statbytes[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# USB status monitor feature:\n",
    "PhyWhisperer can be programmed to log a match on any of the 5 USB status lines. Again, these are:\n",
    "- rx active (bit 0)\n",
    "- rx error (bit 1)\n",
    "- session valid (bit 2)\n",
    "- session end (bit 3)\n",
    "- vbus valid (bit 4)\n",
    "\n",
    "(See the [Microchip USB3500 datasheet](https://www.microchip.com/wwwproducts/en/USB3500) for definitions of what these mean).\n",
    "\n",
    "In glitching attacks it may be useful to monitor rxerror.\n",
    "In this example we're not attacking the target, so let's just illustrate a match event on the 'session end' status line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# look only at bit 3 (session end), don't care what the other bits are:\n",
    "phy.set_stat_pattern(pattern=0x8, mask=0x8)\n",
    "# power off target to cause a \"session end\" event:\n",
    "phy.set_power_source('off')\n",
    "assert phy.stat_pattern_matched() == 1, \"oops, didn't see a session end event!\"\n",
    "print(\"Matched USB status lines: %s\" % hex(phy.stat_pattern_match_value))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "phy.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
